/*______________________________________________________________________________
 *
 * Copyright 2005 NORSYS
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * (1) Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 * (2) Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *
 * (3) The name of the author may not be used to endorse or promote
 *     products derived from this software without specific prior
 *     written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *______________________________________________________________________________
 *
 * Created on 27 sept. 2005
 * Author: Arnaud Bailly
 */
package speculoos.jndi;

import java.util.HashMap;
import java.util.Map;
import java.util.Stack;

import javax.naming.directory.DirContext;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;

import speculoos.beans.MapEntry;
import speculoos.beans.SyntheticMap;
import speculoos.config.xml.TagHandler;
import speculoos.config.xml.XMLConfigurator;
import speculoos.core.ChainMapper;
import speculoos.core.Mapper;
import speculoos.core.MapperCollector;
import speculoos.core.MapperDefaults;
import speculoos.jndi.mappers.AddMapperImpl;
import speculoos.jndi.mappers.AttributeJoinMapperImpl;
import speculoos.jndi.mappers.CompareMapperImpl;
import speculoos.jndi.mappers.DeleteMapperImpl;
import speculoos.jndi.mappers.JNDIMapper;
import speculoos.jndi.mappers.ModificationsVariables;
import speculoos.jndi.mappers.ModifyMapperImpl;
import speculoos.jndi.mappers.RenameMapperImpl;
import speculoos.jndi.mappers.SearchMapperImpl;
import speculoos.jndi.mappers.StringVariable;
import speculoos.jndi.pool.JNDIPooledSource;
import speculoos.utils.StrictMap;
import speculoos.utils.TypeHelper;
import speculoos.utils.VariableString;
import speculoos.valid.CompositeValidator;
import speculoos.valid.MapMandatoryValidator;
import speculoos.valid.RegexValidator;
import speculoos.valid.Validator;

/**
 * A class that allows configuration of the JNDI mapping sub-system using an XML
 * file.
 * 
 * @author nono
 * @version $Id: JNDIXMLConfigurator.java 74 2005-10-25 15:43:05Z
 *          /C=FR/ST=Nord/L=Lille/O=Norsys SA/OU=UE/CN=Arnaud
 *          Bailly/emailAddress=abailly@norsys.fr $
 */
public class JNDIXMLConfigurator extends XMLConfigurator {

	public class JoinHandler implements TagHandler {

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			if ("join".equals(localName)) {
				String name = attributes.getValue("name");

				AttributeJoinMapperImpl join = new AttributeJoinMapperImpl(name);
				join.setTypeHelper(typeHelper);
				push(join);
			} else if ("right".equals(localName)) {
				String name = attributes.getValue("mapper");
				try {
					SearchMapper sm = (SearchMapper) getMapper(name);
					if (sm == null)
						throw new SAXException("Reference to undefined mapper "
								+ name);
					String aj = attributes.getValue("join");
					((AttributeJoinMapperImpl) current).setJoinedAttribute(aj);
					((AttributeJoinMapperImpl) current).setRight(sm);
				} catch (ClassCastException e) {
					throw new SAXException("Invalid reference " + name
							+ " to something not a mapper");
				}
			} else if ("left".equals(localName)) {
				String name = attributes.getValue("mapper");
				try {
					SearchMapper sm = (SearchMapper) getMapper(name);
					if (sm == null)
						throw new SAXException("Reference to undefined mapper "
								+ name);
					String aj = attributes.getValue("join");
					((AttributeJoinMapperImpl) current).setJoinAttribute(aj);
					((AttributeJoinMapperImpl) current).setLeft(sm);
				} catch (ClassCastException e) {
					throw new SAXException("Invalid reference " + name
							+ " to something not a mapper");
				}
			}
		}

		public void endElement(String uri, String localName, String qName)
				throws SAXException {
			if ("join".equals(localName)) {
				AttributeJoinMapperImpl smi = (AttributeJoinMapperImpl) pop();
				add(smi.getName(), smi);
				((JNDISource) current).add(smi.getName(), smi);
				link(smi.getName(), ((JNDISource) current).getName());
			}
		}
	}

	class ControlHandler implements TagHandler {

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			// NOP
		}

		public void endElement(String uri, String localName, String qName)
				throws SAXException {
			String f = getBuffer();
			StringVariable flt = new StringVariable(localName,
					new VariableString(f));
			((JNDIMapper) current).getInputChain().addMapper(flt);
		}

	}

	/**
	 * @author nono
	 * 
	 */
	public class ChainHandler implements TagHandler {

		/*
		 * (non-Javadoc)
		 * 
		 * @see speculoos.config.xml.TagHandler#startElement(java.lang.String,
		 *      java.lang.String, java.lang.String, org.xml.sax.Attributes)
		 */
		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			String mn = attributes.getValue("name");
			ChainMapper cm = new ChainMapper();
			cm.setName(mn);
			/* add chain registered chains */
			maps.put(mn, cm);
			push(cm);
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see speculoos.config.xml.TagHandler#endElement(java.lang.String,
		 *      java.lang.String, java.lang.String)
		 */
		public void endElement(String uri, String localName, String qName)
				throws SAXException {
			pop();
		}

	}

	private TypeHelper typeHelper = new TypeHelper();

	/**
	 * A handler for the <code>bean</code> tag.
	 * 
	 * @author nono
	 * 
	 */
	public class BeanHandler implements TagHandler {

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			String name = attributes.getValue("name");
			String type = attributes.getValue("type");
			/* try to lad class */
			Class cls = null;
			try {
				cls = typeHelper.getClass(type);
			} catch (ClassNotFoundException e) {
				throw new SAXException("The type " + type
						+ " is unknown for bean map " + name, e);
			}
			maps.put(name, cls);
		}

		public void endElement(String uri, String localName, String qName)
				throws SAXException {
			// NOP
		}

	}

	public class RegexValidHandler implements TagHandler {

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			RegexValidator rv = new RegexValidator();
			String inull = attributes.getValue("ignoreNull");
			if (inull != null)
				rv.setIgnoreNull(Boolean.valueOf(inull).booleanValue());
			rv.setPattern(attributes.getValue("value"));
			rv.setProperty(attributes.getValue("key"));
			rv.setTypeHelper(typeHelper);
			push(rv);
		}

		public void endElement(String uri, String localName, String qName)
				throws SAXException {
			Validator v = (Validator) pop();
			((MapperCollector) current).addMapper(v);
		}

	}

	public class DefaultsValidHandler implements TagHandler {

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			MapperDefaults v = new MapperDefaults();
			String name = attributes.getValue("id");
			/* retrieve reference to map */
			String inull = attributes.getValue("map");
			try {
				Map def = (Map) maps.get(inull);
				if (def == null)
					throw new SAXException("Reference to undeclared map "
							+ inull + " in validator " + name);
				/* set defaults */
				v.setDefaults(def);
				v.setTypeHelper(typeHelper);
			} catch (ClassCastException e) {
				throw new SAXException("Reference to " + inull
						+ " is not a map");
			}
			push(v);
		}

		public void endElement(String uri, String localName, String qName)
				throws SAXException {
			Mapper v = (Mapper) pop();
			((MapperCollector) current).addMapper(v);
		}

	}

	public class MandatoryValidHandler implements TagHandler {

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			MapMandatoryValidator v = new MapMandatoryValidator();
			String name = attributes.getValue("id");
			/* retrieve reference to map */
			String inull = attributes.getValue("map");
			Map def = (Map) maps.get(inull);
			if (def == null)
				throw new SAXException("Reference to undeclared map " + inull
						+ " in validator " + name);
			v.setMandatory(def);
			v.setTypeHelper(typeHelper);
			push(v);
		}

		public void endElement(String uri, String localName, String qName)
				throws SAXException {
			Validator v = (Validator) pop();
			((MapperCollector) current).addMapper(v);
		}

	}

	public class NotValidHandler implements TagHandler {

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			push(new CompositeValidator(CompositeValidator.NEGATIVE));
		}

		public void endElement(String uri, String localName, String qName)
				throws SAXException {
			/* pop */
			CompositeValidator cv = (CompositeValidator) pop();
			((MapperCollector) current).addMapper(cv);
		}

	}

	public class OrValidHandler implements TagHandler {

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			push(new CompositeValidator(CompositeValidator.EXISTENTIAL));
		}

		public void endElement(String uri, String localName, String qName)
				throws SAXException {
			/* pop */
			CompositeValidator cv = (CompositeValidator) pop();
			((MapperCollector) current).addMapper(cv);
		}
	}

	public class AndValidHandler implements TagHandler {

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			push(new CompositeValidator(CompositeValidator.UNIVERSAL));
		}

		public void endElement(String uri, String localName, String qName)
				throws SAXException {
			/* pop */
			CompositeValidator cv = (CompositeValidator) pop();
			((MapperCollector) current).addMapper(cv);
		}
	}

	public class CompareHandler implements TagHandler {

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			CompareMapperImpl smi = new CompareMapperImpl(attributes
					.getValue("name"));
			smi.setTypeHelper(typeHelper);
			/* defined input/output map ? */
			Object input = maps.get(attributes.getValue("input"));
			Object output = maps.get(attributes.getValue("output"));
			if (output != null) {
				try {
					smi.setFilter((Class) input, (Map) output);
				} catch (ClassCastException e) {
					throw new SAXException(
							"Incorrect types for input/output attributes in search map. Output should be a map and input a bean.");
				}
			}
			push(smi);
		}

		public void endElement(String uri, String localName, String qName) {
			// add mapper to source
			CompareMapperImpl smi = (CompareMapperImpl) pop();
			add(smi.getName(), smi);
			((JNDISource) current).add(smi.getName(), smi);
			link(smi.getName(), ((JNDISource) current).getName());
		}

	}

	public class RenameHandler implements TagHandler {

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			if ("rename".equals(localName)) {
				RenameMapperImpl smi = new RenameMapperImpl(attributes
						.getValue("name"));
				smi.setTypeHelper(typeHelper);
				push(smi);
			}
		}

		public void endElement(String uri, String localName, String qName) {
			if ("rename".equals(localName)) {
				// add mapper to source
				RenameMapperImpl smi = (RenameMapperImpl) pop();
				add(smi.getName(), smi);
				((JNDISource) current).add(smi.getName(), smi);
				link(smi.getName(), ((JNDISource) current).getName());
			} else if ("newRDN".equals(localName)) {
				StringVariable flt = new StringVariable(localName,
						new VariableString(getBuffer()));
				((JNDIMapper) current).getInputChain().addMapper(flt);
			}
		}

	}

	public class DeleteHandler implements TagHandler {

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			DeleteMapperImpl ami = new DeleteMapperImpl(attributes
					.getValue("name"));
			ami.setTypeHelper(typeHelper);
			push(ami);
		}

		public void endElement(String uri, String localName, String qName) {
			// add mapper to source
			DeleteMapperImpl ami = (DeleteMapperImpl) pop();
			add(ami.getName(), ami);
			((JNDISource) current).add(ami.getName(), ami);
			link(ami.getName(), ((JNDISource) current).getName());
		}

	}

	public class AddHandler implements TagHandler {

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			AddMapperImpl ami = new AddMapperImpl(attributes.getValue("name"));
			ami.setTypeHelper(typeHelper);
			/* defined output map ? */
			Object input = maps.get(attributes.getValue("input"));
			Object output = maps.get(attributes.getValue("output"));
			if (output != null)
				try {
					ami.setOutput((Class) input, (Map) output);
				} catch (ClassCastException e) {
					throw new SAXException(
							"Incorrect types for input/output attributes in search map. Output should be a map and input a bean.");
				}
			push(ami);
		}

		public void endElement(String uri, String localName, String qName) {
			// add mapper to source
			AddMapperImpl ami = (AddMapperImpl) pop();
			add(ami.getName(), ami);
			((JNDISource) current).add(ami.getName(), ami);
			link(ami.getName(), ((JNDISource) current).getName());

		}

	}

	class FilterHandler implements TagHandler {

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			/* defined output map ? */
			Object input = maps.get(attributes.getValue("input"));
			Object output = maps.get(attributes.getValue("output"));
			if (input != null && output != null) {
				SearchMapperImpl smi = ((SearchMapperImpl) current);
				try {
					smi.setFilter((Class) input, (Map) output);
				} catch (ClassCastException e) {
					throw new SAXException(
							"Incorrect types for input/output attributes in search '"
									+ attributes.getValue("name")
									+ "'. Output should be a map and input a bean.");
				}
			}
		}

		public void endElement(String uri, String localName, String qName) {
			String f = getBuffer();
			if (!"".equals(f)) {
				StringVariable flt = new StringVariable(localName,
						new VariableString(f));
				((JNDIMapper) current).getInputChain().addMapper(flt);
			}
		}

	}

	class SearchHandler implements TagHandler {

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			SearchMapperImpl smi = new SearchMapperImpl(attributes
					.getValue("name"));
			smi.setTypeHelper(typeHelper);
			/* defined input/output map ? */
			Object output = maps.get(attributes.getValue("output"));
			Object input = maps.get(attributes.getValue("input"));
			if (output != null && input != null) {
				try {
					smi.setOutput((Class) input, (Map) output);
				} catch (ClassCastException e) {
					throw new SAXException(
							"Incorrect types for input/output attributes in search '"
									+ attributes.getValue("name")
									+ "'. Output should be a map and input a bean.");
				}
			}
			push(smi);
		}

		public void endElement(String uri, String localName, String qName)
				throws SAXException {
			// add mapper to source
			SearchMapperImpl smi = (SearchMapperImpl) pop();
			add(smi.getName(), smi);
			((JNDISource) current).add(smi.getName(), smi);
			link(smi.getName(), ((JNDISource) current).getName());
		}

	}

	public class OperationHandler implements TagHandler {

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			String an = attributes.getValue("name");
			String op = attributes.getValue("op");
			String ref = attributes.getValue("ref");
			String lenient = attributes.getValue("lenient");
			int opcode = DirContext.REPLACE_ATTRIBUTE;
			if (op == null || "modify".equals(op)) // default to modify
				opcode = DirContext.REPLACE_ATTRIBUTE;
			else if ("add".equals(op))
				opcode = DirContext.ADD_ATTRIBUTE;
			else if ("delete".equals(op))
				opcode = DirContext.REMOVE_ATTRIBUTE;
			else
				throw new SAXException(
						"Invalid value for 'op' attribute in tag 'operation'");
			ModificationsVariables.AttributeAndOperation ao = new ModificationsVariables.AttributeAndOperation(
					an, opcode);
			push(new Object[] {ao, ref, Boolean.valueOf(lenient)});
		}

		public void endElement(String uri, String localName, String qName) {
			// retrieve tag content
			String val = getBuffer();
			Object[] poped = (Object[]) pop();
			ModificationsVariables.AttributeAndOperation ao = (ModificationsVariables.AttributeAndOperation) poped [0];
			String ref = (String) poped[1];
			if (ref == null) {
				// value specified with a variable string
				VariableString vs =  new VariableString(val);
				vs.setIgnoreMissing(((Boolean)poped[2]).booleanValue());
				((ModifyMapperImpl) current).operation(ao, vs);	
			} else {
				// value specified with an object reference
				((ModifyMapperImpl) current).operation(ao, ref);
			}
			
		}

	}

	public class ModifyHandler implements TagHandler {

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			ModifyMapperImpl smi = new ModifyMapperImpl(attributes
					.getValue("name"));
			smi.setTypeHelper(typeHelper);
			Object input = maps.get(attributes.getValue("input"));
			Object output = maps.get(attributes.getValue("output"));
			if (output != null) {
				try {
					if (input != null)
						smi.setOutput((Class) input, (Map) output);
					else
						smi.setOperations(new ModificationsVariables(
								(Map) output));
				} catch (ClassCastException e) {
					throw new SAXException(
							"Incorrect types for input/output attributes in search map. Output should be a map and input a bean.");
				}
			}
			push(smi);
		}

		public void endElement(String uri, String localName, String qName) {
			// add mapper to source
			ModifyMapperImpl smi = (ModifyMapperImpl) pop();
			add(smi.getName(), smi);
			((JNDISource) current).add(smi.getName(), smi);
			link(smi.getName(), ((JNDISource) current).getName());
		}
	}

	public class RootHandler implements TagHandler {

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
		}

		public void endElement(String uri, String localName, String qName) {
			String root = getBuffer();
			((JNDIMapper) current).setRoot(new VariableString(root));
		}

	}

	public class PropertyHandler implements TagHandler {

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			((Map) current).put(attributes.getValue("key"), attributes
					.getValue("val"));
		}

		public void endElement(String uri, String localName, String qName) {
		}

	}

	public class SourceHandler implements TagHandler {

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			String name = attributes.getValue("name");
			String pools = attributes.getValue("pooled");
			JNDISource src;
			if (pools != null && pools.equals("true"))
				src = new JNDIPooledSource(name);
			else
				src = new JNDISource(name);
			add(src.getName(), src);
			// append all defined validators
			src.addMappers(getMappers());
			push(src);
		}

		public void endElement(String uri, String localName, String qName) {
			pop();
		}

	}

	/* map from tag to handlers */
	private Map handlers = new HashMap();

	/* stacked contexts in parsing */
	private Stack contexts = new Stack();

	/* current object */
	private Object current;

	/* map of maps */
	private Map /* <String,Map> */maps = new StrictMap(new HashMap());

	/*
	 * default namespace of this configurator
	 */
	private String defaultNamespace = "http://norsys.fr/framework-ldap/jndiconfiguration.dtd";

	class MapHandler implements TagHandler {

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			SyntheticMap m = new SyntheticMap();
			String mn = attributes.getValue("name");
			/* inherited fields to map */
			String inh = attributes.getValue("inherits");
			if (inh != null) {
				String[] refs = inh.split("\\s+");
				for (int i = 0; i < refs.length; i++) {
					Object rm = maps.get(refs[i]);
					if (rm == null)
						throw new SAXException("Undefined map reference "
								+ refs[i] + " in definition for map " + mn);
					m.addInherits(rm);
				}
			}
			/* add map to source */
			maps.put(mn, m);
			push(m);
		}

		public void endElement(String uri, String localName, String qName) {
			pop();
		}

	}

	/*
	 * Handles attributes within maps
	 */
	class EntryHandler implements TagHandler {

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			String k = attributes.getValue("key");
			MapEntry e = new MapEntry(k);
			e.setValue(attributes.getValue("val"));
			e.setType(attributes.getValue("type"));
			((SyntheticMap) current).put(k, e);
		}

		public void endElement(String uri, String localName, String qName)
				throws SAXException {
		}

	}

	class ConfigHandler implements TagHandler {

		public void startElement(String uri, String localName, String qName,
				Attributes attributes) throws SAXException {
			push(new HashMap());
		}

		public void endElement(String uri, String localName, String qName) {
			Map m = (Map) pop();
			// add properties to current source
			((JNDISource) current).addParameters(m);
		}

	}

	/**
	 * Default constructor. Populates handlers.
	 * 
	 */
	public JNDIXMLConfigurator() {
		PropertyHandler ph = new PropertyHandler();
		// for backward compatibility of descriptors
		handlers.put("jndi-mapper", new TagHandler() {

			public void endElement(String uri, String localName, String qName)
					throws SAXException {
				// NOP
			}

			public void startElement(String uri, String localName,
					String qName, Attributes attributes) throws SAXException {
				// NOP
			}

		});
		handlers.put("map", new MapHandler());
		handlers.put("attr", ph);
		handlers.put("bean", new BeanHandler());
		handlers.put("source", new SourceHandler());
		handlers.put("config", new ConfigHandler());
		handlers.put("property", ph);
		// validation rules
		handlers.put("and", new AndValidHandler());
		handlers.put("or", new OrValidHandler());
		handlers.put("not", new NotValidHandler());
		handlers.put("regex", new RegexValidHandler());
		handlers.put("defaults", new DefaultsValidHandler());
		handlers.put("mandatory", new MandatoryValidHandler());
		// chains
		ChainHandler ch = new ChainHandler();
		handlers.put("chain", ch);
		// basic
		handlers.put("root", new RootHandler());
		// modify
		handlers.put("modify", new ModifyHandler());
		handlers.put("operations", new TagHandler() {

			public void endElement(String uri, String localName, String qName)
					throws SAXException {
				ModificationsVariables mods = (ModificationsVariables) pop();
				((JNDIMapper) current).getInputChain().addMapper(mods);
			}

			public void startElement(String uri, String localName,
					String qName, Attributes attributes) throws SAXException {
				push(new ModificationsVariables(((Map) maps.get(attributes
						.getValue("output")))));
			}

		});
		handlers.put("operation", new OperationHandler());
		// search
		SearchHandler sc = new SearchHandler();
		handlers.put("filter", new FilterHandler());
		ControlHandler coh = new ControlHandler();
		handlers.put("search", sc);
		handlers.put("countLimit", coh);
		handlers.put("searchScope", coh);
		handlers.put("timeLimit", coh);
		handlers.put("derefLinkFlag", coh);
		handlers.put("returningObjFlag", coh);
		// add operation
		handlers.put("add", new AddHandler());
		// delete
		handlers.put("delete", new DeleteHandler());
		// rename
		RenameHandler rn = new RenameHandler();
		handlers.put("rename", rn);
		handlers.put("newRDN", rn);
		// handlers.put("deleteOldRdn", rn);
		// handlers.put("newSuperior", rn);
		// compare
		handlers.put("compare", new CompareHandler());
		// join
		JoinHandler jh =  new JoinHandler();
		handlers.put("join",jh);
		handlers.put("left",jh);
		handlers.put("right",jh);
		push(this);
	}

	private void push(Object object) {
		contexts.push(current);
		current = object;
	}

	private Object pop() {
		Object cur = current;
		current = contexts.pop();
		return cur;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.xml.sax.ContentHandler#endElement(java.lang.String,
	 *      java.lang.String, java.lang.String)
	 */
	public void endElement(String uri, String localName, String qName)
			throws SAXException {
		TagHandler th = (TagHandler) handlers.get(localName);
		th.endElement(uri, localName, qName);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.xml.sax.ContentHandler#startElement(java.lang.String,
	 *      java.lang.String, java.lang.String, org.xml.sax.Attributes)
	 */
	public void startElement(String uri, String localName, String qName,
			Attributes attributes) throws SAXException {
		TagHandler th = (TagHandler) handlers.get(localName);
		if (th == null)
			throw new SAXException("Don't know how to handle tag " + localName);
		th.startElement(uri, localName, qName, attributes);
	}

	/**
	 * @return Returns the defaultNamespace.
	 */
	public String getDefaultNamespace() {
		return defaultNamespace;
	}

	/**
	 * @param defaultNamespace
	 *            The defaultNamespace to set.
	 */
	public void setDefaultNamespace(String defaultNamespace) {
		this.defaultNamespace = defaultNamespace;
	}

}
